package meng.study.loadBalancing;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SimpleDemo {

	public static HashMap<String, Integer> serverWeightMap = new HashMap<String, Integer>();
	static {
		serverWeightMap.put("192.168.1.100", 1);
		serverWeightMap.put("192.168.1.101", 2);
		serverWeightMap.put("192.168.1.102", 5);
		/*
		 * serverWeightMap.put("192.168.1.103", 1);
		 * serverWeightMap.put("192.168.1.104", 1);
		 * serverWeightMap.put("192.168.1.105", 3);
		 * serverWeightMap.put("192.168.1.106", 1);
		 * serverWeightMap.put("192.168.1.107", 2);
		 * serverWeightMap.put("192.168.1.108", 1);
		 * serverWeightMap.put("192.168.1.109", 1);
		 * serverWeightMap.put("192.168.1.110", 1);
		 */
	}
	private static Integer pos = 0;

	/**
	 * 轮询（Round Robin） 轮询调度算法的原理是每一次把来自用户的请求轮流分配给内部中的服务器，
	 * 从1开始，直到N(内部服务器个数)，然后重新开始循环。 算法的优点是其简洁性，它无需记录当前所有连接的状态，所以它是一种无状态调度。
	 * 
	 * @return
	 */
	public static String getServerForRoundRobin() {
		// 重建一个Map，避免服务器的上下线导致的并发问题
		Map<String, Integer> serverMap = new HashMap<String, Integer>();
		serverMap.putAll(serverWeightMap);
		// 取得Ip地址List
		Set<String> keySet = serverMap.keySet();

		ArrayList<String> keyList = new ArrayList<String>();
		keyList.addAll(keySet);
		String server = null;
		synchronized (pos) {
			if (pos >= keySet.size())
				pos = 0;
			server = keyList.get(pos);
			pos++;
		}
		return server;
	}

	/**
	 * 随机
	 * 
	 * @return
	 */
	public static String getServerForRandom() {
		Map<String, Integer> serverMap = new HashMap<String, Integer>();
		serverMap.putAll(serverWeightMap);
		Set<String> keySet = serverMap.keySet();
		ArrayList<String> keyList = new ArrayList<String>();
		keyList.addAll(keySet);

		java.util.Random random = new java.util.Random();
		int randomPos = random.nextInt(keyList.size());
		return keyList.get(randomPos);
	}

	/**
	 * <p>
	 * 源地址哈希的思想是根据获取客户端的IP地址，通过哈希函数计算得到的一个数值，
	 * 用该数值对服务器列表的大小进行取模运算，得到的结果便是客服端要访问服务器的序号。
	 * 采用源地址哈希法进行负载均衡，同一IP地址的客户端，当后端服务器列表不变时， 它每次都会映射到同一台后端服务器进行访问。
	 * </p>
	 * 
	 * @return
	 */
	public static String getServerForHash() { // 重建一个Map，避免服务器的上下线导致的并发问题
		Map<String, Integer> serverMap = new HashMap<String, Integer>();
		serverMap.putAll(serverWeightMap); // 取得Ip地址List
		Set<String> keySet = serverMap.keySet();
		ArrayList<String> keyList = new ArrayList<String>();
		keyList.addAll(keySet); // 在Web应用中可通过HttpServlet的getRemoteIp方法获取
		String remoteIp = "127.0.0.2";
		int hashCode = remoteIp.hashCode();
		int serverListSize = keyList.size();
		int serverPos = hashCode % serverListSize;
		return keyList.get(serverPos);
	}

	/**
	 * 加权轮询（Weight Round Robin）法 不同的后端服务器可能机器的配置和当前系统的负载并不相同，因此它们的抗压能力也不相同。
	 * 给配置高、负载低的机器配置更高的权重，让其处理更多的请；而配置低、负载高的机器，
	 * 给其分配较低的权重，降低其系统负载，加权轮询能很好地处理这一问题，并将请求顺序且按照权重分配到后端。
	 * 
	 * @return
	 */
	public static String getServerForWeightRoundRobin() { 
		Map<String, Integer> serverMap = new HashMap<String, Integer>();
		serverMap.putAll(serverWeightMap); 
		Set<String> keySet = serverMap.keySet();
		Iterator<String> iterator = keySet.iterator();

		List<String> serverList = new ArrayList<String>();
		while (iterator.hasNext()) {
			String server = iterator.next();
			int weight = serverMap.get(server);
			for (int i = 0; i < weight; i++)
				serverList.add(server);
		}
		String server = null;
		synchronized (pos) {
			if (pos > keySet.size())
				pos = 0;
			server = serverList.get(pos);
			pos++;
		}
		return server;
	}

	public static void main(String[] args) {
		for (int i = 0; i < 20; i++) {
			System.out.println(getServerForWeightRoundRobin());
		}

	}

}
